using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace Nate.GUI
{
    using Properties;

    public partial class PathingControl : UserControl
    {
        public PathingControl()
        {
            ICanvasControl c = new Canvas.NavMesh();

            _runAStar         = true;
            _update_strategy  = EUpdateStrategy.Stepped;
            _pathfinding      = new Shared.Manager();
            _canvas_factory   = new NUtils.Factory<ICanvasControl>();
            _stepped_timer_interval = 1;

            InitializeComponent();

            NUtils.Console.AddStringTrace(new NUtils.Console.SetString(PathingControl_ConsoleString), NUtils.Console.Levels.ALL);

            this.SuspendLayout();
            foreach (string name in _canvas_factory.Names)
            {
                if (name.Equals(typeof(Canvas.Base).Name))
                {
                    // Ignore the base object.
                    continue;
                }

                toolStripComboBoxCanvasType.Items.Add(name);
            }
            this.ResumeLayout(false);

            toolStripComboBoxCanvasType.SelectedIndex = 0;
        }

        // 
        // Private Methods
        //

        private void _restartPathfinding()
        {
            _pathfinding = null;
            _pathfinding = new Shared.Manager();
        }

        void _initializeCanvas()
        {
            this.SuspendLayout();
            this.toolStripSide.Items.Clear();
            this.toolStripComboBoxCanvasType.Enabled = true;

            if (null != _canvas)
            {
                _canvas.TermCanvas();
                _canvas = null;
                System.GC.Collect();
            }

            _canvas = _canvas_factory.Create(toolStripComboBoxCanvasType.SelectedItem as string);
            _canvas.InitCanvas(panelCanvas, toolStripSide);
            _canvas.Blackboard.GuiBB.AlgorithmName = toolStripComboBoxAlgorithm.Text;
            _restartPathfinding();

            this.ResumeLayout(false);
        }

        void _initializePathingOptions()
        {
            int old_index = toolStripComboBoxPathingOptions.SelectedIndex;
            toolStripLabelPathingOptions.Text = "";
            toolStripComboBoxPathingOptions.ToolTipText = "";
            toolStripComboBoxPathingOptions.Items.Clear();
            toolStripComboBoxPathingOptions.Text = "";


            if (null == _canvas.Blackboard.PathingBB.Options)
            {
                toolStripLabelPathingOptions.Enabled = false;
                toolStripComboBoxPathingOptions.Enabled = false;
                return;
            }

            toolStripLabelPathingOptions.Enabled = true;
            toolStripComboBoxPathingOptions.Enabled = true;
            toolStripLabelPathingOptions.Text = _canvas.Blackboard.PathingBB.Options.Label;
            toolStripComboBoxPathingOptions.Items.AddRange(_canvas.Blackboard.PathingBB.Options.Selections);
            toolStripComboBoxPathingOptions.ToolTipText = _canvas.Blackboard.PathingBB.Options.ToolTipText;

            if (old_index < toolStripComboBoxPathingOptions.Items.Count &&
                old_index > 0)
            {
                toolStripComboBoxPathingOptions.SelectedIndex = old_index;
            }
            else
            {
                _canvas.Blackboard.GuiBB.SelectedOption = toolStripComboBoxPathingOptions.Items[0] as string;
                toolStripComboBoxPathingOptions.Text = _canvas.Blackboard.GuiBB.SelectedOption;
            }
        }

        // 
        // Event/Delegate Handlers
        //

        private void PathingControl_ConsoleString(string s)
        {
            this.autoScrollBox.Text += s + "\n";
        }

        private void PathingControl_Load(object sender, EventArgs e)
        {
            timer.Start();
            string[] algo_names = Shared.Utils.AlgorithmNames;
            System.Array.Sort(algo_names);
            this.toolStripComboBoxAlgorithm.Items.AddRange(algo_names);
            this.toolStripComboBoxAlgorithm.Text = this.toolStripComboBoxAlgorithm.Items[0] as string;
            _canvas.Blackboard.GuiBB.AlgorithmName = toolStripComboBoxAlgorithm.Text;

            this.toolStripComboBoxUpdateRate.Items.Add(EUpdateStrategy.Stepped.ToString());
            this.toolStripComboBoxUpdateRate.Items.Add(EUpdateStrategy.Immediate.ToString());
            this.toolStripComboBoxUpdateRate.SelectedIndex = 0;

            // toolStripButtonEnable.CheckState = CheckState.Unchecked;
        }

        private void toolStripComboBoxPathingOptions_TextChanged(object sender, EventArgs e)
        {
            _canvas.Blackboard.GuiBB.SelectedOption = toolStripComboBoxPathingOptions.Text;
        }

        private void toolStripComboBoxUpdateRate_SelectedIndexChanged(object sender, EventArgs e)
        {
            string rate = toolStripComboBoxUpdateRate.SelectedItem as string;
            if (rate.Equals(EUpdateStrategy.Immediate.ToString()))
            {
                _update_strategy = EUpdateStrategy.Immediate;
                timer.Interval = 1;
                toolStripButtonRateUp.Enabled = false;
                toolStripButtonRateDown.Enabled = false;
            }
            else
            {
                _update_strategy = EUpdateStrategy.Stepped;
                timer.Interval = _stepped_timer_interval;
                
                toolStripButtonRateUp.Enabled = _stepped_timer_interval > 1;
                toolStripButtonRateDown.Enabled = true;
            }
        }

        private void toolStripButtonRateUp_Click(object sender, EventArgs e)
        {
            // The logic is kinda backwards. If the user presses "up" he/she
            // expects the updates to go faster, which means lowering the 
            // timer interval.

            _stepped_timer_interval -= _stepped_timer_interval_inc;

            if (_stepped_timer_interval <= 1)
            {
                toolStripButtonRateUp.Enabled = false;
                _stepped_timer_interval = 1;
            }

            timer.Interval = _stepped_timer_interval;
        }

        private void toolStripButtonRateDown_Click(object sender, EventArgs e)
        {
            // The logic is kinda backwards. If the user presses "down" he/she
            // expects the updates to go slower, which means raising the timer
            // interval.

            _stepped_timer_interval += _stepped_timer_interval_inc;
            timer.Interval = _stepped_timer_interval;
            toolStripButtonRateUp.Enabled = true;

        }

        private void toolStripComboBoxAlgorithm_SelectedIndexChanged(object sender, EventArgs e)
        {
            _canvas.Blackboard.GuiBB.AlgorithmName = toolStripComboBoxAlgorithm.SelectedItem as string;
            _canvas.Blackboard.PathingBB.Options = null;
        }
        
        private void toolStripButtonPause_CheckStateChanged(object sender, EventArgs e)
        {
            if (toolStripButtonPause.CheckState == CheckState.Checked)
            {
                _runAStar = false;
                toolStripButtonPause.Text = "Click again to resume pathfinding";
                return;
            }

            _runAStar = true;
            toolStripButtonPause.Text = "Click to pause pathfinding";
        }
        
        private void toolStripComboBoxCanvasType_SelectedIndexChanged(object sender, EventArgs e)
        {
            _initializeCanvas();
        }

        private void toolStripButtonRestart_Click(object sender, EventArgs e)
        {
            _restartPathfinding();
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            timer.Start();

            if (!_runAStar)
            {
                return;
            }

            if (EUpdateStrategy.Immediate == _update_strategy)
            {
                // Nate-TODO. This trick to generate the immediate result is
                // making the app unresponsive. Very tacky. Workout something
                // where only X ticks are process or only use up Y milliseconds
                // before bailing and update the draw.

                while (_pathfinding.Update(_canvas.Blackboard.GuiBB) &&
                       _pathfinding.PathingBB.Status == Shared.Status.InProcess)
                {
                }
            }
            else
            {
                _pathfinding.Update(_canvas.Blackboard.GuiBB);
            }

            _canvas.Blackboard.PathingBB.Path    = _pathfinding.PathingBB.Path;
            _canvas.Blackboard.PathingBB.Status  = _pathfinding.PathingBB.Status;

            if (null == _pathfinding.PathingBB.Options ||
                _canvas.Blackboard.PathingBB.Options != _pathfinding.PathingBB.Options)
            {
                _canvas.Blackboard.PathingBB.Options = _pathfinding.PathingBB.Options;
                _initializePathingOptions();
            }

            _canvas.RefreshCanvas();
        }

        // 
        // Private Types
        //

        enum EUpdateStrategy
        {
            Stepped,
            Immediate
        }

        // 
        // Private Data
        //

        private Shared.Manager    _pathfinding;
        private bool              _runAStar;
        private EUpdateStrategy   _update_strategy;
        private ICanvasControl    _canvas;
        private int               _stepped_timer_interval;
        private const int         _stepped_timer_interval_inc = 10;
        private const float       _zoom_amount = 0.1f;

        private NUtils.Factory<ICanvasControl> _canvas_factory;
    }
}
